{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Performance comparison"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import xgboost\n",
    "import numpy as np\n",
    "import shap\n",
    "import time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from iml.common import convert_to_instance, convert_to_model, match_instance_to_data, match_model_to_data, convert_to_instance_with_index\n",
    "from iml.explanations import AdditiveExplanation\n",
    "from iml.links import convert_to_link, IdentityLink\n",
    "from iml.datatypes import convert_to_data, DenseData\n",
    "import logging\n",
    "from iml.explanations import AdditiveExplanation\n",
    "\n",
    "log = logging.getLogger('shap')\n",
    "from shap import KernelExplainer\n",
    "class IMEExplainer(KernelExplainer):\n",
    "    \"\"\" This is an implementation of the IME explanation method (aka. Shapley sampling values)\n",
    "    \n",
    "    IME was proposed in \"An Efficient Explanation of Individual Classifications using Game Theory\",\n",
    "    Erik Štrumbelj, Igor Kononenko, JMLR 2010\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self, model, data, **kwargs):\n",
    "        # silence warning about large datasets\n",
    "        level = log.level\n",
    "        log.setLevel(logging.ERROR)\n",
    "        super(IMEExplainer, self).__init__(model, data, **kwargs)\n",
    "        log.setLevel(level)\n",
    "    \n",
    "    def explain(self, incoming_instance, **kwargs):\n",
    "        # convert incoming input to a standardized iml object\n",
    "        instance = convert_to_instance(incoming_instance)\n",
    "        match_instance_to_data(instance, self.data)\n",
    "        \n",
    "        # pick a reasonable number of samples if the user didn't specify how many they wanted\n",
    "        self.nsamples = kwargs.get(\"nsamples\", 0)\n",
    "        if self.nsamples == 0:\n",
    "            self.nsamples = 1000 * self.P\n",
    "        \n",
    "        # divide up the samples among the features\n",
    "        self.nsamples_each = np.ones(self.P, dtype=np.int64) * 2 * (self.nsamples // (self.P * 2))\n",
    "        for i in range((self.nsamples % (self.P * 2)) // 2):\n",
    "            self.nsamples_each[i] += 2\n",
    "        \n",
    "        model_out = self.model.f(instance.x)\n",
    "        \n",
    "        # explain every feature\n",
    "        phi = np.zeros(self.P)\n",
    "        self.X_masked = np.zeros((self.nsamples_each.max(), X.shape[1]))\n",
    "        for i in range(self.P):\n",
    "            phi[i] = self.ime(i, self.model.f, instance.x, self.data.data, nsamples=self.nsamples_each[i])\n",
    "        phi = np.array(phi)\n",
    "        \n",
    "        return AdditiveExplanation(self.link.f(1), self.link.f(1), phi, np.zeros(len(phi)), instance, self.link,\n",
    "                                   self.model, self.data)\n",
    "        \n",
    "        \n",
    "    def ime(self, j, f, x, X, nsamples=10):\n",
    "        assert nsamples % 2 == 0, \"nsamples must be divisible by 2!\"\n",
    "        X_masked = self.X_masked[:nsamples,:]\n",
    "        inds = np.arange(X.shape[1])\n",
    "\n",
    "        for i in range(0, nsamples//2):\n",
    "            np.random.shuffle(inds)\n",
    "            pos = np.where(inds == j)[0][0]\n",
    "            rind = np.random.randint(X.shape[0])\n",
    "            X_masked[i,:] = x\n",
    "            X_masked[i,inds[pos+1:]] = X[rind,inds[pos+1:]]\n",
    "            X_masked[-(i+1),:] = x\n",
    "            X_masked[-(i+1),inds[pos:]] = X[rind,inds[pos:]]\n",
    "            \n",
    "        evals = f(X_masked)\n",
    "        \n",
    "        evals_on = evals[:nsamples//2]\n",
    "        evals_off = evals[nsamples//2:][::-1]\n",
    "        \n",
    "        return np.mean(evals[:nsamples//2] - evals[nsamples//2:])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 10/10 [00:59<00:00,  6.11s/it]\n",
      "  0%|          | 0/10 [00:00<?, ?it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TreeExplainer 0.5667633771896362\n",
      "KernelExplainer 1.6998610496520996\n",
      "IMEExplainer 36.06944036483765\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 10/10 [00:43<00:00,  4.03s/it]\n",
      "  0%|          | 0/10 [00:00<?, ?it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TreeExplainer 0.5962720394134522\n",
      "KernelExplainer 2.410202980041504\n",
      "IMEExplainer 24.168269634246826\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 10/10 [04:23<00:00, 26.14s/it]\n",
      "  0%|          | 0/10 [00:00<?, ?it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TreeExplainer 0.5785245656967163\n",
      "KernelExplainer 3.6281538009643555\n",
      "IMEExplainer 170.16926431655884\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 10/10 [06:38<00:00, 41.04s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TreeExplainer 0.5870000839233398\n",
      "KernelExplainer 5.659902095794678\n",
      "IMEExplainer 254.79452514648438\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "from tqdm import tqdm\n",
    "\n",
    "tree_shap_times = []\n",
    "kernel_shap_times = []\n",
    "ime_times = []\n",
    "nreps = 10\n",
    "\n",
    "N = 1000\n",
    "X_full = np.random.randn(N, 20)\n",
    "y = np.random.randn(N)\n",
    "\n",
    "for M in range(4,8):\n",
    "    ts = []\n",
    "    tree_shap_time = 0\n",
    "    kernel_shap_time = 0\n",
    "    ime_time = 0\n",
    "    for k in tqdm(range(nreps)):\n",
    "#         print()\n",
    "         #+ ((X > 0).sum(1) % 2)\n",
    "        X = X_full[:,:M]\n",
    "\n",
    "        model = xgboost.train({\"eta\": 1}, xgboost.DMatrix(X, y), 1000)\n",
    "\n",
    "        def f(x):\n",
    "            return model.predict(xgboost.DMatrix(x))\n",
    "\n",
    "\n",
    "        start = time.time()\n",
    "        shap_values = shap.TreeExplainer(model).shap_values(X)\n",
    "        tree_shap_time += time.time() - start\n",
    "#         print(\"Tree SHAP:\", tree_shap_time, \"seconds\")\n",
    "\n",
    "        shap_stddev = shap_values.std(0)[:-1].mean()\n",
    "\n",
    "#         print(\"mean std dev of SHAP values over samples:\", shap_stddev)\n",
    "\n",
    "        e = shap.KernelExplainer(f, X.mean(0).reshape(1,M))\n",
    "        nsamples = 200\n",
    "#         print(shap_stddev/20)\n",
    "        for j in range(2000):\n",
    "            #print(nsamples)\n",
    "            start = time.time()\n",
    "            std_dev = np.vstack([e.shap_values(X[:1,:], silent=True, nsamples=nsamples) for i in range(50)]).std(0)[:-1].mean()\n",
    "            iter_time = (time.time() - start)/50\n",
    "            #print(std_dev)\n",
    "            if std_dev < shap_stddev/20:\n",
    "#                 print(\"KernelExplainer\", nsamples)\n",
    "#                 print(\"KernelExplainer\", std_dev)\n",
    "#                 print(\"KernelExplainer\", iter_time, \"seconds\")\n",
    "                kernel_shap_time += iter_time * 1000\n",
    "                break\n",
    "            nsamples += int(nsamples * 0.5)\n",
    "\n",
    "        e = IMEExplainer(f, X.mean(0).reshape(1,M))\n",
    "        nsamples = 200\n",
    "        for j in range(2000):\n",
    "        #     print()\n",
    "        #     print(nsamples)\n",
    "            start = time.time()\n",
    "            std_dev = np.vstack([e.shap_values(X[:1,:], silent=True, nsamples=nsamples) for i in range(50)]).std(0)[:-1].mean()\n",
    "        #     print(\"time\", (time.time() - start)/50)\n",
    "        #     print(std_dev)\n",
    "            iter_time = (time.time() - start)/50\n",
    "            if std_dev < shap_stddev/20:\n",
    "#                 print(\"IMEExplainer\", nsamples)\n",
    "#                 print(\"IMEExplainer\", std_dev)\n",
    "#                 print(\"IMEExplainer\", iter_time, \"seconds\")\n",
    "                ime_time += iter_time * 1000\n",
    "                break\n",
    "            nsamples += int(nsamples * 0.5)\n",
    "\n",
    "    tree_shap_times.append(tree_shap_time / nreps)\n",
    "    kernel_shap_times.append(kernel_shap_time / nreps)\n",
    "    ime_times.append(ime_time / nreps)\n",
    "    print(\"TreeExplainer\", tree_shap_times[-1])\n",
    "    print(\"KernelExplainer\", kernel_shap_times[-1])\n",
    "    print(\"IMEExplainer\", ime_times[-1])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.035476692"
      ]
     },
     "execution_count": 96,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.predict(xgboost.DMatrix(X)).mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.35859036,  0.02820558, -0.0797407 , ...,  0.257011  ,\n",
       "         0.14601958, -0.03547668],\n",
       "       [-0.13089252,  0.05794828, -0.25855556, ...,  0.13599055,\n",
       "         0.05735664, -0.03547668],\n",
       "       [-0.06776153,  0.10805923, -0.15713027, ..., -0.03505304,\n",
       "        -0.126518  , -0.03547668],\n",
       "       ...,\n",
       "       [ 0.2136814 ,  0.26080823, -0.28865245, ...,  0.37536186,\n",
       "         0.0192902 , -0.03547668],\n",
       "       [-0.26988652, -0.4866938 ,  0.08402091, ..., -0.9819344 ,\n",
       "         0.10559861, -0.03547668],\n",
       "       [ 0.2289274 ,  0.34278524,  0.04976799, ...,  0.17089835,\n",
       "        -0.2589223 , -0.03547668]], dtype=float32)"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "shap.TreeExplainer(model).shap_values(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.03536954622922005"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "e = shap.KernelExplainer(f, X.mean(0).reshape(1,M))\n",
    "np.vstack([e.shap_values(X[:1,:], silent=True, nsamples=100) for i in range(50)]).std(0)[:-1].mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.01028014048933983\n",
      "200\n",
      "time 0.017876300811767578\n",
      "0.030893178278808088\n",
      "240\n",
      "time 0.01951019763946533\n",
      "0.031826491957925064\n",
      "288\n",
      "time 0.020209121704101562\n",
      "0.02404333546749491\n",
      "345\n",
      "time 0.026800222396850586\n",
      "0.02191601539734573\n",
      "414\n",
      "time 0.03098787784576416\n",
      "0.019460386139078127\n",
      "496\n",
      "time 0.035751018524169925\n",
      "0.016387066172673846\n",
      "595\n",
      "time 0.04151914119720459\n",
      "0.014285933327475487\n",
      "714\n",
      "time 0.04223478317260742\n",
      "0.007730075891320169\n",
      "714\n"
     ]
    }
   ],
   "source": [
    "e = shap.KernelExplainer(f, X.mean(0).reshape(1,M))\n",
    "nsamples = 200\n",
    "print(shap_stddev/20)\n",
    "for j in range(2000):\n",
    "    print(nsamples)\n",
    "    start = time.time()\n",
    "    std_dev = np.vstack([e.shap_values(X[:1,:], silent=True, nsamples=nsamples) for i in range(50)]).std(0)[:-1].mean()\n",
    "    iter_time = time.time() - start)/50\n",
    "    print(std_dev)\n",
    "    if std_dev < shap_stddev/20:\n",
    "        print(nsamples)\n",
    "        break\n",
    "    nsamples += int(nsamples * 0.2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.01028014048933983\n",
      "200\n",
      "time 0.00873462200164795\n",
      "0.11283049739292224\n",
      "240\n",
      "time 0.008001022338867188\n",
      "0.11367197853719405\n",
      "288\n",
      "time 0.011501140594482422\n",
      "0.1015653587243526\n",
      "345\n",
      "time 0.009341177940368652\n",
      "0.09609185923926047\n",
      "414\n",
      "time 0.011126718521118163\n",
      "0.09085558541161791\n",
      "496\n",
      "time 0.013986682891845703\n",
      "0.07316596561338537\n",
      "595\n",
      "time 0.015461082458496095\n",
      "0.07250843647631118\n",
      "714\n",
      "time 0.01717233657836914\n",
      "0.06397322007857624\n",
      "856\n",
      "time 0.022870540618896484\n",
      "0.05976986733191383\n",
      "1027\n",
      "time 0.023901219367980956\n",
      "0.05245597953754562\n",
      "1232\n",
      "time 0.027827000617980956\n",
      "0.04745224231486926\n",
      "1478\n",
      "time 0.03509308338165283\n",
      "0.044717441080350424\n",
      "1773\n",
      "time 0.03991847991943359\n",
      "0.04005476391599552\n",
      "2127\n",
      "time 0.049641480445861814\n",
      "0.03812949961499427\n",
      "2552\n",
      "time 0.055928120613098146\n",
      "0.035810585105934746\n",
      "3062\n",
      "time 0.06629895687103271\n",
      "0.03023017573129038\n",
      "3674\n",
      "time 0.08427309989929199\n",
      "0.028151972927120038\n",
      "4408\n",
      "time 0.09465731620788574\n",
      "0.0269187187096272\n",
      "5289\n",
      "time 0.11998224258422852\n",
      "0.022239860334659148\n",
      "6346\n",
      "time 0.14299814224243165\n",
      "0.021938981389563468\n",
      "7615\n",
      "time 0.16221713542938232\n",
      "0.018939869158391444\n",
      "9138\n",
      "time 0.19562265872955323\n",
      "0.017731913242184698\n",
      "10965\n",
      "time 0.2298459815979004\n",
      "0.016393279654396718\n",
      "13158\n",
      "time 0.274345440864563\n",
      "0.014646114662805663\n",
      "15789\n",
      "time 0.3319106578826904\n",
      "0.013088638305933157\n",
      "18946\n",
      "time 0.39471452236175536\n",
      "0.012587389782775047\n",
      "22735\n",
      "time 0.47068305969238283\n",
      "0.010845919708344176\n",
      "27282\n",
      "time 0.5693935012817383\n",
      "0.009881009462255178\n",
      "27282\n"
     ]
    }
   ],
   "source": [
    "e = IMEExplainer(f, X.mean(0).reshape(1,M))\n",
    "nsamples = 200\n",
    "print(shap_stddev/20)\n",
    "for j in range(2000):\n",
    "    print()\n",
    "    print(nsamples)\n",
    "    start = time.time()\n",
    "    std_dev = np.vstack([e.shap_values(X[:1,:], silent=True, nsamples=nsamples) for i in range(50)]).std(0)[:-1].mean()\n",
    "    print(\"time\", (time.time() - start)/50)\n",
    "    print(std_dev)\n",
    "    if std_dev < shap_stddev/20:\n",
    "        print(nsamples)\n",
    "        break\n",
    "    nsamples += int(nsamples * 0.2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "569.39"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "0.56939 * 1000"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.10860389655048514"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.std([IMEExplainer(f, X.mean(0).reshape(1,M)).shap_values(X[:1,:], silent=True, nsamples=1000)[0,0] for i in range(10)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "[-0.023676379073087367, -0.016465743042426484, -0.01704981192550009, -0.018463699458183006, -0.015413553059016158, -0.018366587057752404, -0.018345955714953288, -0.01926604949186531, -0.020396613851721868, -0.022111201288591047, -0.021292876352076523, -0.016925113704097466, -0.02094528676946239, -0.017590427283719918, -0.019455733580809376, -0.01867957831829911, -0.01949199924313576, -0.02101184177451282, -0.02017175661418666, -0.02096841698600091, -0.018895762603090914, -0.019425187391885285, -0.018764209898906616, -0.02034981563789895, -0.016667910548829656, -0.019427677621697337, -0.019561391198267386, -0.02373383328585584, -0.017769291765458606, -0.014731135458020739, -0.01941898747203586, -0.020089057050320602, -0.019028685125618505, -0.01962223838514693, -0.014568940935971698, -0.01879752859462427, -0.018104240312813935, -0.019211399989558977, -0.021056719830586573, -0.018778172837663646, -0.017658694005700082, -0.019683240102995762, -0.0162219930410617, -0.019259564972963772, -0.01949005847120361, -0.020469469407407792, -0.012816015402021158, -0.017980626203507558, -0.020897377427076758, -0.01868522102558161, -0.019841164347362744, -0.01599499867003788, -0.023435164030154998, -0.021409903581716072, -0.021450264083101744, -0.019248117394281006, -0.019173267845621733, -0.021201344451321935, -0.013850440640304496, -0.021287986511504587, -0.017783326099606533, -0.019453412161091754, -0.01843054464371413, -0.017254967740485454, -0.019103215848530974, -0.01879518729784055, -0.016519074933045257, -0.020527604263832898, -0.014484539065507975, -0.019919451452777043, -0.01791397307200243, -0.014867680956663631, -0.018727430647644053, -0.018632469437516508, -0.018405491524922116, -0.01747364031491877, -0.01699568669949486, -0.01874634675402474, -0.01885201916934609, -0.02082471371013129, -0.018097378494376464, -0.020461013612397938, -0.01658913152329026, -0.021541127531476613, -0.021776620067082225, -0.020021620037416813, -0.01784317502978247, -0.02140662002036492, -0.02027189405855656, -0.021590607084915926, -0.018937654008019444, -0.01834817726407234, -0.01792343162344523, -0.02115901867004244, -0.019107028260820377, -0.02001855177068948, -0.019976099903129818, -0.013791710928271679, -0.021506799835474072, -0.01541739020484878]"
      ],
      "text/plain": [
       "[-0.023676379073087367,\n",
       " -0.016465743042426484,\n",
       " -0.01704981192550009,\n",
       " -0.018463699458183006,\n",
       " -0.015413553059016158,\n",
       " -0.018366587057752404,\n",
       " -0.018345955714953288,\n",
       " -0.01926604949186531,\n",
       " -0.020396613851721868,\n",
       " -0.022111201288591047,\n",
       " -0.021292876352076523,\n",
       " -0.016925113704097466,\n",
       " -0.02094528676946239,\n",
       " -0.017590427283719918,\n",
       " -0.019455733580809376,\n",
       " -0.01867957831829911,\n",
       " -0.01949199924313576,\n",
       " -0.02101184177451282,\n",
       " -0.02017175661418666,\n",
       " -0.02096841698600091,\n",
       " -0.018895762603090914,\n",
       " -0.019425187391885285,\n",
       " -0.018764209898906616,\n",
       " -0.02034981563789895,\n",
       " -0.016667910548829656,\n",
       " -0.019427677621697337,\n",
       " -0.019561391198267386,\n",
       " -0.02373383328585584,\n",
       " -0.017769291765458606,\n",
       " -0.014731135458020739,\n",
       " -0.01941898747203586,\n",
       " -0.020089057050320602,\n",
       " -0.019028685125618505,\n",
       " -0.01962223838514693,\n",
       " -0.014568940935971698,\n",
       " -0.01879752859462427,\n",
       " -0.018104240312813935,\n",
       " -0.019211399989558977,\n",
       " -0.021056719830586573,\n",
       " -0.018778172837663646,\n",
       " -0.017658694005700082,\n",
       " -0.019683240102995762,\n",
       " -0.0162219930410617,\n",
       " -0.019259564972963772,\n",
       " -0.01949005847120361,\n",
       " -0.020469469407407792,\n",
       " -0.012816015402021158,\n",
       " -0.017980626203507558,\n",
       " -0.020897377427076758,\n",
       " -0.01868522102558161,\n",
       " -0.019841164347362744,\n",
       " -0.01599499867003788,\n",
       " -0.023435164030154998,\n",
       " -0.021409903581716072,\n",
       " -0.021450264083101744,\n",
       " -0.019248117394281006,\n",
       " -0.019173267845621733,\n",
       " -0.021201344451321935,\n",
       " -0.013850440640304496,\n",
       " -0.021287986511504587,\n",
       " -0.017783326099606533,\n",
       " -0.019453412161091754,\n",
       " -0.01843054464371413,\n",
       " -0.017254967740485454,\n",
       " -0.019103215848530974,\n",
       " -0.01879518729784055,\n",
       " -0.016519074933045257,\n",
       " -0.020527604263832898,\n",
       " -0.014484539065507975,\n",
       " -0.019919451452777043,\n",
       " -0.01791397307200243,\n",
       " -0.014867680956663631,\n",
       " -0.018727430647644053,\n",
       " -0.018632469437516508,\n",
       " -0.018405491524922116,\n",
       " -0.01747364031491877,\n",
       " -0.01699568669949486,\n",
       " -0.01874634675402474,\n",
       " -0.01885201916934609,\n",
       " -0.02082471371013129,\n",
       " -0.018097378494376464,\n",
       " -0.020461013612397938,\n",
       " -0.01658913152329026,\n",
       " -0.021541127531476613,\n",
       " -0.021776620067082225,\n",
       " -0.020021620037416813,\n",
       " -0.01784317502978247,\n",
       " -0.02140662002036492,\n",
       " -0.02027189405855656,\n",
       " -0.021590607084915926,\n",
       " -0.018937654008019444,\n",
       " -0.01834817726407234,\n",
       " -0.01792343162344523,\n",
       " -0.02115901867004244,\n",
       " -0.019107028260820377,\n",
       " -0.02001855177068948,\n",
       " -0.019976099903129818,\n",
       " -0.013791710928271679,\n",
       " -0.021506799835474072,\n",
       " -0.01541739020484878]"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[shap.KernelExplainer(f, X.mean(0).reshape(1,M)).shap_values(X[:1,:], silent=True, nsamples=1000)[0,0] for i in range(100)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  7%|▋         | 74/1000 [00:04<00:50, 18.27it/s]\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-7-ad2b02590797>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0mstart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mshap_values2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mshap\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mKernelExplainer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mX\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mM\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshap_values\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      6\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mstart\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/Users/slund1/projects/shap/shap/explainers/kernel.py\u001b[0m in \u001b[0;36mshap_values\u001b[0;34m(self, X, **kwargs)\u001b[0m\n\u001b[1;32m    124\u001b[0m                 \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkeep_index\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    125\u001b[0m                     \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconvert_to_instance_with_index\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumn_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex_value\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 126\u001b[0;31m                 \u001b[0mexplanations\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexplain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    127\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    128\u001b[0m             \u001b[0;31m# vector-output\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/Users/slund1/projects/shap/shap/explainers/kernel.py\u001b[0m in \u001b[0;36mexplain\u001b[0;34m(self, incoming_instance, **kwargs)\u001b[0m\n\u001b[1;32m    258\u001b[0m                         \u001b[0;32mif\u001b[0m \u001b[0msubset_size\u001b[0m \u001b[0;34m<=\u001b[0m \u001b[0mnum_paired_subset_sizes\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    259\u001b[0m                             \u001b[0mmask\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmask\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 260\u001b[0;31m                             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maddsample\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minstance\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    261\u001b[0m                 \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    262\u001b[0m                     \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/Users/slund1/projects/shap/shap/explainers/kernel.py\u001b[0m in \u001b[0;36maddsample\u001b[0;34m(self, x, m, w)\u001b[0m\n\u001b[1;32m    346\u001b[0m                         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msynth_data\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0moffset\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    347\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 348\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmaskMatrix\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnsamplesAdded\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    349\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkernelWeights\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnsamplesAdded\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    350\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnsamplesAdded\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "def f(x):\n",
    "    return model.predict(xgboost.DMatrix(x))\n",
    "\n",
    "start = time.time()\n",
    "shap_values2 = shap.KernelExplainer(f, X.mean(0).reshape(1,M)).shap_values(X)\n",
    "print(time.time() - start)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  4%|▍         | 41/1000 [00:13<05:22,  2.97it/s]"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-22-d6ca7fbbc83a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mstart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mIMEExplainer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mX\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mM\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshap_values\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mstart\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/Users/slund1/projects/shap/shap/explainers/kernel.py\u001b[0m in \u001b[0;36mshap_values\u001b[0;34m(self, X, **kwargs)\u001b[0m\n\u001b[1;32m    124\u001b[0m                 \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkeep_index\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    125\u001b[0m                     \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconvert_to_instance_with_index\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumn_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex_value\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 126\u001b[0;31m                 \u001b[0mexplanations\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexplain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    127\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    128\u001b[0m             \u001b[0;31m# vector-output\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-12-b3190dceb824>\u001b[0m in \u001b[0;36mexplain\u001b[0;34m(self, incoming_instance, **kwargs)\u001b[0m\n\u001b[1;32m     45\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mX_masked\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzeros\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnsamples_each\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mX\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     46\u001b[0m         \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mP\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 47\u001b[0;31m             \u001b[0mphi\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minstance\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnsamples\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnsamples_each\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     48\u001b[0m         \u001b[0mphi\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mphi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     49\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-12-b3190dceb824>\u001b[0m in \u001b[0;36mime\u001b[0;34m(self, j, f, x, X, nsamples)\u001b[0m\n\u001b[1;32m     62\u001b[0m             \u001b[0mpos\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwhere\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minds\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     63\u001b[0m             \u001b[0mrind\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 64\u001b[0;31m             \u001b[0mX_masked\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     65\u001b[0m             \u001b[0mX_masked\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0minds\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mpos\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mX\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mrind\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0minds\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mpos\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     66\u001b[0m             \u001b[0mX_masked\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "start = time.time()\n",
    "IMEExplainer(f, X.mean(0).reshape(1,M)).shap_values(X)\n",
    "print(time.time() - start)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
